using System;
using System.Collections.Generic;
using System.Linq;
using Db4objects.Db4o.Linq;
using SharedObjects;
using TheGame.Server;

namespace TheGame.Webservices
{
	public partial class TheGameService
	{
		#region Hardware
		/*
		private bool ReconfigureGateway(ShallowGateway sgw)
		{
			//get Gateway for sgw.
			Gateway gw = (from Gateway g in db where g.ID == sgw.ID select g).FirstOrDefault();
			if (gw == null)
				return false;
			bool isNewGateway = false;
			if (gw.GatewayBlueprint.ID != sgw.BlueprintId)
				isNewGateway = true;

			//get (new) Blueprint
			GatewayBlueprint gwp = (from GatewayBlueprint bp in db where bp.ID == sgw.BlueprintId select bp).FirstOrDefault();

			#region check CPU configuration for max sockets & max allowed cpu speed & max total speed
			double maxSockets = gwp.NumCpuSlots;
			double maxAllowedSpeed = gwp.MaxAllowedCpuSpeed;
			double maxTotalSpeed = gwp.MaxCpuSpeed;

			if (sgw.CpuIds.Count > maxSockets)
				return false;
			List<CPU> newCpus = new List<CPU>();
			foreach (ulong cpuId in sgw.CpuIds)
			{
				CPU configuredCpu = (from CPU cpu in db where cpu.ID == cpuId select cpu).FirstOrDefault();
				if (configuredCpu == null)
					return false;
				if (configuredCpu.Modifier > maxAllowedSpeed)
					return false;
				newCpus.Add(configuredCpu);
			}
			if (newCpus.Sum(it => it.Modifier) > maxTotalSpeed)
				return false;
			#endregion

			#region check Memory configuration
			int maxAllowedSize = gwp.MaxAllowedMemorySize;
			int maxTotalSize = gwp.MaxMemorySize;
			int numMemSlots = gwp.NumMemorySlots;
			if (sgw.MemoryIds.Count > numMemSlots)
				return false;
			List<MemoryType> newMemories = new List<MemoryType>();
			foreach (ulong memId in sgw.MemoryIds)
			{
				MemoryType mem = (from MemoryType m in db where m.ID == memId select m).FirstOrDefault();
				if (mem == null)
					return false;
				if (mem.Modifier > maxAllowedSize)
					return false;
				newMemories.Add(mem);
			}
			if (newMemories.Sum(it => it.Modifier) > maxTotalSize)
				return false;
			#endregion

			#region check NIC configuration
			double maxAllowedBandwidth = gwp.MaxAllowedBandwidth;
			double maxTotalBandwidth = gwp.MaxBandwidth;
			int numNicSlots = gwp.NumNicSlots;
			if (sgw.NicIds.Count > numNicSlots)
				return false;
			List<NetworkAdapter> newNics = new List<NetworkAdapter>();
			foreach (ulong id in sgw.NicIds)
			{
				NetworkAdapter nwa = (from NetworkAdapter n in db where n.ID == id select n).FirstOrDefault();
				if (nwa == null)
					return false;
				if (nwa.Modifier > maxAllowedBandwidth)
					return false;
				newNics.Add(nwa);
			}
			if (newNics.Sum(it => it.Modifier) > maxTotalBandwidth)
				return false;
			#endregion

			gw.InstalledMemory = newMemories;
			gw.InstalledNics = newNics;
			gw.InstalledCpus = newCpus;

			//the new blueprint is different from the old one, so we assign the new blueprint to the old gateway, thus assigning the new configuration limits to the old gateway. no real switchover required.
			if (isNewGateway)
				gw.GatewayBlueprint = gwp;

			db.PersistEntity(gw);
			db.Commit();
			return true;
		}
		*/

		public ShallowGateway GetGatewayDetails(Guid sessionId)
		{
			Agent me;
			if(!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}
			var agent = (from Agent a in Db where a.Id == me.Id select a).FirstOrDefault();
			if (agent == null)
			{
				SendServerError(ServerErrorSeverity.Fatal, "Error fetching agent for this session id!");
				return null;
			}

			return agent.Gateway;
		}

		public GatewayBlueprint GetGatewayBlueprintDetails(Guid sessionId, ulong blueprintId)
		{
			Agent me;
			if(!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}
			var ret = (from GatewayBlueprint b in Db where b.Id == blueprintId select b).FirstOrDefault();
			return ret;
		}

		public List<EntityBase> GetCurrentHardware(Guid sessionId)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}
			Db.Ext().Refresh(me, 1000);
			Gateway myGw = (from Gateway gw in Db where gw.Id == me.Gateway.Id select gw).FirstOrDefault();
			if(myGw == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No gateway found for you.");
				return null;
			}
			List<EntityBase> retVal = (from x in myGw.InstalledCpus select x).Cast<EntityBase>().ToList();

			retVal.AddRange(from x in myGw.InstalledMemory select x);
			retVal.AddRange(from x in myGw.InstalledNics select x);
			retVal.Add(me.Gateway.GatewayBlueprint);
			return retVal;
		}

		public List<EntityBase> GetStoredHardware(Guid sessionId)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}
			var agent = (from Agent a in Db where a.Id == me.Id select a).FirstOrDefault();
			if (agent == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Agent not found for session id.");
				return null;
			}
			return agent.OwnedHardware.Cast<EntityBase>().ToList();
		}

		public bool ScheduleGatewayReconfiguration(Guid sessionId, ShallowGateway sgw, bool immediate)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return false;
			}

			var agent = (from Agent a in Db where a.Id == me.Id select a).First();

			Gateway gw = (from Gateway g in Db where g.Id == agent.Gateway.Id select g).FirstOrDefault();
			if (gw == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Gateway not found for this agent.");
				return false;
			}
			//first of all, move all installed hardware to storage, since we rebuild the machine from scratch (easier than differential upgrade)
			agent.OwnedHardware.AddRange(gw.InstalledCpus);
			gw.InstalledCpus.Clear();
			agent.OwnedHardware.AddRange(gw.InstalledMemory);
			gw.InstalledMemory.Clear();
			agent.OwnedHardware.AddRange(gw.InstalledNics);
			gw.InstalledNics.Clear();

			GatewayBlueprint gwp = (from GatewayBlueprint x in Db where x.Id == sgw.BlueprintId select x).FirstOrDefault();
			if (gwp == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Specified gateway blueprint not found.");
				return false;
			}
			
			//1. Make sure user has the listed hardware actually in possession in his storedhardware.
			//var uniqueIds = sgw.CpuIds.Distinct();
			Func<IEnumerable<ulong>, bool> checkSufficientStock = ids =>
			{
				foreach (ulong uniqueId in ids.Distinct())
				{
					ulong id = uniqueId;
					int num = ids.Count(it => it == id);
					var items = (from Agent a in Db where a.Id == agent.Id from EntityBase hw in a.OwnedHardware where hw.Id == id select hw);
					if (items.Count() < num)
					{
						SendServerError(ServerErrorSeverity.Error, "You do not own the specified hardware. Configuration aborted.");
						return false;
					}
				}
				return true;
			};
			if (!checkSufficientStock(sgw.CpuIds))
				return false;
			if (!checkSufficientStock(sgw.MemoryIds))
				return false;
			if (!checkSufficientStock(sgw.NicIds))
				return false;

			#region make sure the listed configuration is valid with the used blueprint
			#region CPU
			List<Cpu> installedCpus = new List<Cpu>();
			foreach (ulong cpuId in sgw.CpuIds)
			{
				ulong id = cpuId;
				Cpu cpu = (from Cpu x in Db where x.Id == id select x).FirstOrDefault();
				if (cpu == null)
				{
					SendServerError(ServerErrorSeverity.Error, "Specified CPU not found.");
					return false;
				}
				installedCpus.Add(cpu);
			}
			double totalSpeed = installedCpus.Sum(it => it.Modifier);
			double maxSingleSpeed = installedCpus.Max(it => it.Modifier);
			if (totalSpeed > gwp.MaxCpuSpeed)
			{
				SendServerError(ServerErrorSeverity.Error, "Configured total speed exceeds allowed maximum.");
				return false;
			}
			if (maxSingleSpeed > gwp.MaxAllowedCpuSpeed)
			{
				SendServerError(ServerErrorSeverity.Error, "One or more CPUs had a speed faster than the supported maximum per-cpu speed");
				return false;
			}
			if (installedCpus.Count > gwp.NumCpuSlots)
			{
				SendServerError(ServerErrorSeverity.Error, "Number of configured CPUs exceeds number of sockets.");
				return false;
			}
			#endregion

			#region Memory
			List<MemoryType> installedMem = new List<MemoryType>();
			foreach (ulong memId in sgw.MemoryIds)
			{
				ulong id = memId;
				MemoryType mem = (from MemoryType x in Db where x.Id == id select x).FirstOrDefault();
				if (mem == null)
				{
					SendServerError(ServerErrorSeverity.Error, "Specified memory type not found.");
					return false;
				}
				installedMem.Add(mem);
			}
			double totalCapacity = installedMem.Sum(it => it.Modifier);
			double maxSingleCapacity = installedMem.Max(it => it.Modifier);
			if (totalCapacity > gwp.MaxMemorySize)
			{
				SendServerError(ServerErrorSeverity.Error, "Total capacity of memory exceeds allowed maximum.");
				return false;
			}
			if (maxSingleCapacity > gwp.MaxAllowedMemorySize)
			{
				SendServerError(ServerErrorSeverity.Error, "Allowed per-memory size was exceeded at least once.");
				return false;
			}
			if (installedMem.Count > gwp.NumMemorySlots)
			{
				SendServerError(ServerErrorSeverity.Error, "Maximum number of allowed memory modules was exceeded.");
				return false;
			}
			#endregion

			#region NIC
			List<NetworkAdapter> installedNics = new List<NetworkAdapter>();
			foreach (ulong nicId in sgw.NicIds)
			{
				ulong id = nicId;
				NetworkAdapter nwa = (from NetworkAdapter x in Db where x.Id == id select x).FirstOrDefault();
				if (nwa == null)
				{
					SendServerError(ServerErrorSeverity.Error, "Specified network adapter was not found.");
					return false;
				}
				installedNics.Add(nwa);
			}
			double totalBandwidth = installedNics.Sum(it => it.Modifier);
			double maxSingleBandwidth = installedNics.Max(it => it.Modifier);
			if (totalBandwidth > gwp.MaxBandwidth)
			{
				SendServerError(ServerErrorSeverity.Error, "Total bandwidth exceeds allowed maximum.");
				return false;
			}
			if (maxSingleBandwidth > gwp.MaxAllowedBandwidth)
			{
				SendServerError(ServerErrorSeverity.Error, "Allowed per-NIC bandwidth was exceeded at least once.");
				return false;
			}
			if (installedNics.Count > gwp.NumNicSlots)
			{
				SendServerError(ServerErrorSeverity.Error, "Number of installed NICs exceeds number of NIC slots.");
				return false;
			}
			#endregion
			#endregion

			agent.OwnedHardware.Add(gw.GatewayBlueprint);
			agent.OwnedHardware.Remove(gwp);
			gw.GatewayBlueprint = gwp;
			if (installedCpus.Count > 0)
			{
				gw.InstalledCpus.AddRange(installedCpus);
				foreach (Cpu cpu in installedCpus)
					agent.OwnedHardware.Remove(cpu);
			}

			if(installedMem.Count>0)
			{
				gw.InstalledMemory.AddRange(installedMem);
				foreach (MemoryType mem in installedMem)
					agent.OwnedHardware.Remove(mem);
			}


			if (installedNics.Count > 0)
			{
				gw.InstalledNics.AddRange(installedNics);
				foreach (NetworkAdapter nwa in installedNics)
					agent.OwnedHardware.Remove(nwa);
			}
			
			Db.PersistEntity(gw);
			Db.PersistEntity(agent);
			Db.Commit();
			World.Instance.UpdateAgentSessionLifetime(sessionId);
			if (Callback != null)
			{				
				Callback.RebootForHardwareChange();
			}
			return true;
		}
		#endregion
	}
}